/*-------------------------------------------------------------------------
 *
 * regexport.c
 *	  导出关于正则表达式的 NFA 的信息的函数
 *
 * 在该实现中，NFA 定义了字符串匹配正则表达式的必要但不充分的条件：
 * 也就是说，可能存在匹配 NFA 的字符串，但不匹配完整正则表达式的字符串，
 * 但反之则不然。因此，例如，下面的函数可以将环视约束视为无操作，
 * 因为它们仅仅会限制字符串。
 *
 * 请注意，这些函数将信息返回到调用方提供的数组中，而不是自己进行内存分配。
 * 这简化了 API，通过消除一类错误条件，而在颜色的情况下允许调用者决定
 * 多大才算过大。
 *
 *
 * Portions Copyright (c) 2013-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1998, 1999 Henry Spencer
 *
 * IDENTIFICATION
 *	  src/backend/regex/regexport.c
 *
 *-------------------------------------------------------------------------
 */

#include "regex/regguts.h"

#include "regex/regexport.h"


/*
 * 获取NFA状态的总数。
 */
int pg_reg_getnumstates(const regex_t *fc_regex)
{
	struct cnfa *fc_cnfa;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cnfa = &((struct guts *) fc_regex->re_guts)->search;

	return fc_cnfa->nstates;
}

/*
 * 获取NFA的初始状态。
 */
int pg_reg_getinitialstate(const regex_t *fc_regex)
{
	struct cnfa *fc_cnfa;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cnfa = &((struct guts *) fc_regex->re_guts)->search;

	return fc_cnfa->pre;
}

/*
 * 获取NFA的最终状态。
 */
int pg_reg_getfinalstate(const regex_t *fc_regex)
{
	struct cnfa *fc_cnfa;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cnfa = &((struct guts *) fc_regex->re_guts)->search;

	return fc_cnfa->post;
}

/*
 * pg_reg_getnumoutarcs()和pg_reg_getoutarcs()隐藏了LACON
 * 弧线的存在，将任何LACON视为自动满足。
 * 由于输出表示并不支持遍历时不消耗字符的弧线，我们必须在这里递归地遍历LACON弧线，
 * 并报告通过遍历LACON弧线可达的正常弧线。
 * 请注意，如果通过LACON遍历可以到达最终状态，这样是行不通的，
 * 但正则表达式库从不构建有直接通向最终状态的LACON弧线的NFA。（这是因为
 * 正则表达式执行器被设计为在名义匹配结束之后消耗一个字符——可能是EOS指示符——
 * 因此总有一组普通弧线通向最终状态。）
 *
 * traverse_lacons是一个递归子例程，由两个导出函数使用，
 * 以计数并然后发出可达的常规弧线。 *arcs_count的值
 * 由可达弧线的数量增加，最多可以容纳在arcs_len中
 * （可能为0），被发出到arcs[]中。
 */
static void fc_traverse_lacons(struct cnfa *fc_cnfa, int fc_st,
				int *fc_arcs_count,
				regex_arc_t *fc_arcs, int fc_arcs_len)
{
	struct carc *fc_ca;

	/*
	 * 由于这个函数是递归的，从理论上讲，它可能会导致栈
	 * 溢出。 实际上，这主要是有用的，以防正则表达式编译器
	 * 无法删除LACON弧线的循环。
	 */
	check_stack_depth();

	for (fc_ca = fc_cnfa->states[fc_st]; fc_ca->co != COLORLESS; fc_ca++)
	{
		if (fc_ca->co < fc_cnfa->ncolors)
		{
			/* 普通弧线，因此计数并可能发出它 */
			int			fc_ndx = (*fc_arcs_count)++;

			if (fc_ndx < fc_arcs_len)
			{
				fc_arcs[fc_ndx].co = fc_ca->co;
				fc_arcs[fc_ndx].to = fc_ca->to;
			}
		}
		else
		{
			/* LACON弧线——假设它已满足并递归... */
			/* ...但首先，确保它不直接通向后置状态 */
			Assert(fc_ca->to != fc_cnfa->post);

			fc_traverse_lacons(fc_cnfa, fc_ca->to, fc_arcs_count, fc_arcs, fc_arcs_len);
		}
	}
}

/*
 * 获取状态编号“st”的出弧线数量。
 */
int pg_reg_getnumoutarcs(const regex_t *fc_regex, int fc_st)
{
	struct cnfa *fc_cnfa;
	int			fc_arcs_count;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cnfa = &((struct guts *) fc_regex->re_guts)->search;

	if (fc_st < 0 || fc_st >= fc_cnfa->nstates)
		return 0;
	fc_arcs_count = 0;
	fc_traverse_lacons(fc_cnfa, fc_st, &fc_arcs_count, NULL, 0);
	return fc_arcs_count;
}

/*
 * 将状态编号“st”的出弧线数组写入arcs[]，
 * 其长度arcs_len必须至少与pg_reg_getnumoutarcs()指示的长度相同，
 * 否则将无法返回所有弧线。
 */
void pg_reg_getoutarcs(const regex_t *fc_regex, int fc_st,
				  regex_arc_t *fc_arcs, int fc_arcs_len)
{
	struct cnfa *fc_cnfa;
	int			fc_arcs_count;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cnfa = &((struct guts *) fc_regex->re_guts)->search;

	if (fc_st < 0 || fc_st >= fc_cnfa->nstates || fc_arcs_len <= 0)
		return;
	fc_arcs_count = 0;
	fc_traverse_lacons(fc_cnfa, fc_st, &fc_arcs_count, fc_arcs, fc_arcs_len);
}

/*
 * 获取颜色的总数。
 */
int pg_reg_getnumcolors(const regex_t *fc_regex)
{
	struct colormap *fc_cm;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cm = &((struct guts *) fc_regex->re_guts)->cmap;

	return fc_cm->max + 1;
}

/*
 * 检查颜色是否为行/字符串的开头。
 *
 * （我们可能在某个时候需要提供更精细的伪颜色处理，
 * 但这暂时可以满足。）
 */
int pg_reg_colorisbegin(const regex_t *fc_regex, int fc_co)
{
	struct cnfa *fc_cnfa;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cnfa = &((struct guts *) fc_regex->re_guts)->search;

	if (fc_co == fc_cnfa->bos[0] || fc_co == fc_cnfa->bos[1])
		return true;
	else
		return false;
}

/*
 * 检查颜色是否为行/字符串的结尾。
 */
int pg_reg_colorisend(const regex_t *fc_regex, int fc_co)
{
	struct cnfa *fc_cnfa;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cnfa = &((struct guts *) fc_regex->re_guts)->search;

	if (fc_co == fc_cnfa->eos[0] || fc_co == fc_cnfa->eos[1])
		return true;
	else
		return false;
}

/*
 * 获取颜色编号“co”的成员字符数量。
 *
 * 注意：如果颜色编号无效，或如果它是特殊颜色
 * （WHITE、RAINBOW或伪颜色），或者成员的数量不确定，
 * 我们将返回-1。
 * 调用者不应尝试提取成员如果返回-1。
 */
int pg_reg_getnumcharacters(const regex_t *fc_regex, int fc_co)
{
	struct colormap *fc_cm;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cm = &((struct guts *) fc_regex->re_guts)->cmap;

	if (fc_co <= 0 || fc_co > fc_cm->max)	/* <= 0 拒绝 WHITE 和 RAINBOW */
		return -1;
	if (fc_cm->cd[fc_co].flags & PSEUDO)	/* 也拒绝伪颜色（BOS等） */
		return -1;

	/*
	 * 如果颜色在高色图中出现，则将其成员数量视为不确定。
	 * 原则上，我们可以确定每个此类条目的所有特定字符，
	 * 但这将是非常昂贵的
	 * （特别是如果需要字符类测试），而且似乎没有必要。
	 */
	if (fc_cm->cd[fc_co].nuchrs != 0)
		return -1;

	/* 好吧，返回已知的成员字符数量 */
	return fc_cm->cd[fc_co].nschrs;
}

/*
 * 将颜色编号“co”的成员字符数组写入chars[]，
 * 其长度chars_len必须至少与pg_reg_getnumcharacters()指示的长度相同，
 * 否则将无法返回所有字符。
 *
 * 不支持提取WHITE、RAINBOW或伪颜色的成员。
 *
 * 注意：这是一项相对昂贵的操作。
 */
void pg_reg_getcharacters(const regex_t *fc_regex, int fc_co,
					 pg_wchar *fc_chars, int fc_chars_len)
{
	struct colormap *fc_cm;
	chr			fc_c;

	assert(fc_regex != NULL && fc_regex->re_magic == REMAGIC);
	fc_cm = &((struct guts *) fc_regex->re_guts)->cmap;

	if (fc_co <= 0 || fc_co > fc_cm->max || fc_chars_len <= 0)
		return;
	if (fc_cm->cd[fc_co].flags & PSEUDO)
		return;

	/*
	 * 我们只需检查低字符映射；在高映射中不应有任何
	 * 匹配条目。
	 */
	for (fc_c = CHR_MIN; fc_c <= MAX_SIMPLE_CHR; fc_c++)
	{
		if (fc_cm->locolormap[fc_c - CHR_MIN] == fc_co)
		{
			*fc_chars++ = fc_c;
			if (--fc_chars_len == 0)
				break;
		}
	}
}
